/*
 * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email &
 * please set dead value or install git & please set dead value or install git
 * @Date: 2024-11-08 22:02:36
 * @LastEditors: yoola youlikesoilove@qq.com
 * user.email & please set dead value or install git & please set dead value or install git user.email & please set dead
 * value or install git & please set dead value or install git
 * @LastEditTime: 2024-12-24 23:35:13
 * @FilePath: \CrabSystemProject\src\ModuleCenter\moduleComm.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置:
 * https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma once
#include <iostream>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <cstring>
#include <mutex>
#include <memory>
#include <stdint.h>
#include <cstdlib>    // for std::atexit
#include <termios.h>  // speed_t : B9600
#include <thread>
#include <chrono>   // 用于 std::chrono::milliseconds
#include <iomanip>  // For setw and setfill
#include <ctime>
#include <nlohmann/json.hpp>
#include "simpleLoger.hpp"

#define DISABLE_DIRECT_OUTPUT "关闭默认输出，使用日志管理器"

#ifndef DISABLE_DIRECT_OUTPUT
#include <chrono>
#include <iomanip>
#include <sstream>
#define GET_LOCAL_TIME                                                                                      \
    []() {                                                                                                  \
        auto now = std::chrono::system_clock::now();                                                        \
        auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;     \
        std::time_t t = std::chrono::system_clock::to_time_t(now);                                          \
        std::tm tm = *std::localtime(&t);                                                                   \
        std::ostringstream oss;                                                                             \
        oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S.") << std::setw(3) << std::setfill('0') << ms.count(); \
        return oss.str();                                                                                   \
    }()
#define FILE_LINE   " - " << __FILE__ << "[" << __LINE__ << "] - "
#define OUTPUT_ERRR std::cerr << GET_LOCAL_TIME << FILE_LINE
#define OUPUT_INFO  std::cout << GET_LOCAL_TIME << FILE_LINE
#endif

enum class Serial_BaudRate : speed_t {  // 波特率-写入用
    BaudRate_1200 = B1200,
    BaudRate_2400 = B2400,
    BaudRate_4800 = B4800,
    BaudRate_9600 = B9600,
    BaudRate_19200 = B19200,
    BaudRate_38400 = B38400,
    BaudRate_57600 = B57600,
    BaudRate_115200 = B115200
};

class SerialPortBase {
private:
    int bufferSize;
    std::shared_ptr<uint8_t[]> p_recv_data;
    static int fd;            // 静态文件描述符，所有实例共享
    static std::mutex mutex;  // 静态互斥锁，保护共享资源
    static bool port_is_init;

    // 保护构造函数，确保只能通过 getInstance() 创建
    SerialPortBase() : bufferSize(256), p_recv_data(new uint8_t[bufferSize]) {
        // 注册程序退出时自动调用的资源清理函数
        std::atexit([]() { SerialPortBase::getInstance().closePort(); });
    }

    ~SerialPortBase() {
        closePort();
    }

public:
    static Serial_BaudRate m_cur_baudrate;
    // 获取单例实例
    static SerialPortBase& getInstance() {
        static SerialPortBase instance;
        return instance;
    }

    // 禁止拷贝和赋值
    SerialPortBase(const SerialPortBase&) = delete;
    SerialPortBase& operator=(const SerialPortBase&) = delete;

    // 打开串口并配置，返回是否成功
    bool openPort(const char* port, Serial_BaudRate baudRate) {
        if (SerialPortBase::port_is_init == true && baudRate == m_cur_baudrate) {
            return true;
        } else if (baudRate != m_cur_baudrate) {
            OUTPUT_ERRR << "设备列表中不允许出现多个波特率端口" << std::endl;
            return false;
        }

        std::lock_guard<std::mutex> lock(mutex);  // 加锁保护

        if (fd != -1) {
            OUTPUT_ERRR << "端口已被打开" << std::endl;
            return false;
        }

        fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
        if (fd == -1) {
            OUTPUT_ERRR << "无法打开设备端口 " << port << std::endl;
            return false;
        }

        termios tty;
        memset(&tty, 0, sizeof(tty));
        if (tcgetattr(fd, &tty) != 0) {
            OUTPUT_ERRR << "获取串口属性失败" << std::endl;
            closePort();
            return false;
        }

        // 设置波特率和串口配置
        cfsetospeed(&tty, static_cast<speed_t>(baudRate));
        cfsetispeed(&tty, static_cast<speed_t>(baudRate));
        tty.c_cflag = static_cast<speed_t>(baudRate) | CS8 | CLOCAL | CREAD;
        tty.c_iflag = IGNPAR;
        tty.c_oflag = 0;
        tty.c_lflag = 0;

        tcflush(fd, TCIFLUSH);
        if (tcsetattr(fd, TCSANOW, &tty) != 0) {
            OUTPUT_ERRR << "设置串口属性失败" << std::endl;
            closePort();
            return false;
        }
        SerialPortBase::port_is_init = true;
        return true;
    }

    // 发送数据
    bool sendData(std::shared_ptr<uint8_t[]> _p_send_data, int send_size) {
        std::lock_guard<std::mutex> lock(mutex);  // 加锁保护
        if (fd == -1) {
            OUTPUT_ERRR << "端口未打开" << std::endl;
            return false;
        }

        int writeResult = write(fd, _p_send_data.get(), send_size);
        if (writeResult < 0) {
            OUTPUT_ERRR << "数据发送失败" << std::endl;
            return false;
        }

        // OUPUT_INFO << "发送数据成功: " ;
        return true;
    }

    // 接收数据
    bool receiveData(std::shared_ptr<uint8_t[]> p_ret_recv_data, int recv_size) {
        if (recv_size == 0 || recv_size > 256) {
            OUTPUT_ERRR << "输入参数不对，不能接收0字符数据或大于256byte的数据！" << std::endl;
            return false;
        }

        std::lock_guard<std::mutex> lock(mutex);  // 加锁保护
        if (fd == -1) {
            OUTPUT_ERRR << "端口未打开" << std::endl;
            return false;
        }

        memset(p_recv_data.get(), 0, bufferSize);

        int readResult = read(fd, p_recv_data.get(), bufferSize - 1);

        if (readResult < 0) {
            OUTPUT_ERRR << "读取数据失败" << std::endl;
            return false;
        } else if (readResult == 0) {
            OUTPUT_ERRR << "数据读取到文件末尾" << std::endl;
            return false;
        } else if (readResult == recv_size) {
            memcpy(p_ret_recv_data.get(), p_recv_data.get(), readResult);
            // OUPUT_INFO << "数据读取成功！" << std::endl;
            return true;
        } else {
            OUTPUT_ERRR << "警告：读取的字节数 (" << readResult << ") 与预期不匹配 (" << recv_size << ")" << std::endl;
            return false;
        }
        return false;
    }

    // 关闭端口
    void closePort() {
        std::lock_guard<std::mutex> lock(mutex);  // 加锁保护
        if (fd != -1) {
            close(fd);
            fd = -1;
            SerialPortBase::port_is_init = false;
        }
    }
};

class RS485SerialPortBASE {
private:
    static bool m_debug_to_print;
    static bool is_init_SerialPortBase;
    int bufferSize;
    std::shared_ptr<uint8_t[]> m_p_recv_data;
    std::shared_ptr<uint8_t[]> m_p_send_data;
    std::shared_ptr<uint8_t[]> m_p_buffer_data;
    static std::mutex mutex;  // 静态互斥锁，保护共享资源

protected:
    bool get_REGValue(
        const uint16_t& reg_addr,
        const uint16_t& get_Byte_size,
        std::shared_ptr<uint8_t[]>& p_recv_data);                            // 获取定长数据
    bool set_REGValue(const uint16_t& reg_addr, const uint16_t& send_data);  // 写入定长数据
    bool Get_16bitREGValue(const uint16_t& reg_addr, uint16_t& ret_val);     // 获取指定寄存器的16bit数据
    bool Get_32bitREGValue(const uint16_t& reg_addr, uint32_t& ret_val);     // 获取指定寄存器的16bit数据
    virtual bool _ReadData(void) = 0;                                        // 子类的数据实现
    virtual bool _CheckDevOnline(void) = 0;                                  // 检查设备是否在线
public:
    int m_wait_resp_time;
    uint8_t dev_ID;                                                     // 1-247, 0为广播地址，所有设备均能识别
    std::chrono::time_point<std::chrono::system_clock> last_data_time;  // 上次更新数据时间
    const std::string m_module_name;
    enum Func_Reg : uint8_t {
        READ_SINGLE_IO_OUTPUT_REG = 0x01,    // 读从机IO输出值，不支持广播
        READ_SINGLE_IO_INPUT_REG = 0x02,     // 读从机IO输入值，不支持广播
        READ_SINGLE_DATA_OUTPUT_REG = 0x03,  // 读从机输出保持寄存器（模拟数据），不支持广播
        READ_SINGLE_DATA_INPUT_REG = 0x04,   // 读从机输入保持寄存器（模拟数据），不支持广播
        WRITE_SINGLE_DATA_INPUT_REG = 0x06,  // 设置从机单寄存器
        WRITE_MUTI_DATA_INPUT_REG = 0x10,    // 设置从机多寄存器

    };
    uint16_t CRC16_MudBus(uint8_t* puchMsg, uint8_t usDataLen);
    RS485SerialPortBASE(int input_devId, const std::string& module_name, int wait_resp_time = 100) :
            bufferSize(256),
            m_p_recv_data(new uint8_t[bufferSize]),
            m_p_send_data(new uint8_t[bufferSize]),
            m_p_buffer_data(new uint8_t[bufferSize]),
            dev_ID(static_cast<uint8_t>(input_devId)),
            m_wait_resp_time(wait_resp_time),  // 消息回复等待时间
            m_module_name(module_name)         // 模型名称
    {
        // 继承 SerialPortBase 的实例可以在此类中使用基类的功能
        // 比如，初始化串口、配置参数等
    }
    virtual ~RS485SerialPortBASE() = default;
    bool init() {
        return SerialPortBase::getInstance().openPort(
            "/dev/ttyUSB0", Serial_BaudRate::BaudRate_9600);  // 默认为所有的设备设定波特率为9600
    }
    void SetDebugModel(bool debug) {
        m_debug_to_print = debug;
    };
    bool ReadData(void) {
        if (_ReadData()) {
            last_data_time = std::chrono::system_clock::now();  // 更新数据时间
            return true;
        }
        return false;
    };  // 读取子类的数据
    bool CheckDevOnline(void) {
        return _CheckDevOnline();
    };
    // 用于保存当前对象的实际类型信息
    virtual const std::type_info& GetTypeInfo() const = 0;
};

template <typename DataT>
class RS485SerialPort : public RS485SerialPortBASE {
private:
    DataT m_get_data;

public:
    DataT GetData(void) {
        return m_get_data;
    }
    void SetData(DataT data) {
        m_get_data = data;
    }
    // 实现父类的虚函数，返回派生类的类型信息
    const std::type_info& GetTypeInfo() const override {
        return typeid(RS485SerialPort<DataT>);
    }
    RS485SerialPort(int input_devId, const std::string& module_name, int wait_resp_time = 100) :
            RS485SerialPortBASE(input_devId, module_name, wait_resp_time) {};
    virtual ~RS485SerialPort() = default;
};

class Weight_Reader : public RS485SerialPort<float> {
private:
    float m_weight;  // 称重
    virtual bool _ReadData(void) override {
        uint32_t weight = 0;
        if (Get_32bitREGValue(0x00, weight)) {  // 获取物体重量
            m_weight = weight / 10.0;
            SetData(m_weight);
            return true;
        }
        return false;
    };  // 读子设备的数据
    virtual bool _CheckDevOnline(void) override {
        uint32_t ret_dev_id = 0x0;
        Get_32bitREGValue(0X0406, ret_dev_id);  // 查询设备地址
        if ((ret_dev_id & 0xFF) == dev_ID) {
            return true;
        } else {
            OUPUT_INFO << "设备掉线" << std::endl;
            return false;
        }
    };

public:
    Weight_Reader(int input_devId = 0x01) : RS485SerialPort(input_devId, "weight-reader") {};

    void reset(void) {
        set_REGValue(0x00, 0x00);  // 写入校零
        set_REGValue(0x01, 0x00);  // 写入校零
    }
};

class temper_Reader : public RS485SerialPort<float> {
private:
    float m_temperature;  // 称重
    virtual bool _ReadData(void) override {
        uint16_t get_value = 0x0;
        if (Get_16bitREGValue(0x00, get_value))  // 获取原始数据
        {
            // OUPUT_INFO << "读取到的原始数据为 " << get_value << std::endl;
            m_temperature = static_cast<float>(get_value / 10.0);  // 获取转换数据
            SetData(m_temperature);
            return true;
        }
        return false;
    };
    virtual bool _CheckDevOnline(void) override {
        uint16_t ret_dev_id = 0x0;
        Get_16bitREGValue(0x0011, ret_dev_id);  // 查询设备地址
        OUPUT_INFO << "读取到的设备ID" << ret_dev_id << std::endl;
        if ((ret_dev_id & 0xFF) == dev_ID) {
            return true;
        } else {
            OUPUT_INFO << "设备掉线" << std::endl;
            return false;
        }
    };

public:
    temper_Reader(int input_devId = 0x09) :
            RS485SerialPort(input_devId, "temperature-reader", 500) {};  // 温度数据一般要等待500ms

    float GetRealTemper(void) {
        return m_temperature;
    }
};

class IO_Control : public RS485SerialPort<uint8_t> {
private:
    uint8_t m_IO_output;                          // IO输出
    virtual bool _ReadData(void) override {       // 建议只在初始化时读取当前线圈数据，效率比较低
        m_IO_output = 0x00;                       // 复位数据
        uint16_t get_value = 0x0;                 // 创建变量领回数据
        if (!Get_16bitREGValue(0x00, get_value))  // 获取第一个线圈
            return false;                         // 如果没有读取成功
        m_IO_output |= (get_value & 0x01);        // 读取线圈输出数据
        get_value = 0x00;                         // 数据置空
        if (!Get_16bitREGValue(0x01, get_value))
            return false;
        m_IO_output |= ((get_value & 0x01) << 1);  // 读取第二个线圈
        get_value = 0x00;                          // 数据置空
        if (!Get_16bitREGValue(0x02, get_value))
            return false;
        m_IO_output |= ((get_value & 0x01) << 2);  // 读取第三个线圈
        get_value = 0x00;                          // 数据置空
        if (!Get_16bitREGValue(0x03, get_value))
            return false;
        m_IO_output |= ((get_value & 0x01) << 3);  // 读取第四个线圈
        SetData(m_IO_output);
        return true;
    };
    virtual bool _CheckDevOnline(void) override {
        uint16_t ret_dev_id = 0x0;
        Get_16bitREGValue(0x0032, ret_dev_id);  // 查询设备地址
        if ((ret_dev_id & 0xFF) == dev_ID) {
            return true;
        } else {
            OUPUT_INFO << "设备掉线" << std::endl;
            return false;
        }
    };

public:
    IO_Control(int input_devId = 0x05) : RS485SerialPort(input_devId, "IO-controller") {};

    bool SetCoilSwitch(int which_one, bool is_open) {
        if (which_one > 3) {
            OUPUT_INFO << "写入失败，写入线圈大于线圈总数，线圈总数为4个，写入为：" << which_one << std::endl;
            return false;
        }
        if (is_open)
            return set_REGValue(which_one, 0x0001);
        else
            return set_REGValue(which_one, 0x0000);
    }
};
